-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix: Approval Usability #172
Conversation
Warning Rate limit exceeded@zacharyblasczyk has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 23 minutes and 37 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. WalkthroughThis pull request implements significant changes across multiple components related to policy approvals and deployment management. The Changes
Possibly related PRs
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
🧹 Outside diff range and nitpick comments (6)
apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/PolicyApprovalRow.tsx (2)
28-31
: Early return pattern improves error handling.Good addition of environment validation. However, consider enhancing the error message with more context.
- logger.error("Environment is undefined for approval:", approval); + logger.error("Environment is undefined for approval", { + approvalId: approval.id, + releaseId: approval.releaseId, + policyId: approval.policyId + });
112-116
: Consider accessibility improvements.The approval status UI could benefit from ARIA attributes for better screen reader support.
return ( - <div className="flex items-center gap-2 rounded-md text-sm"> + <div + className="flex items-center gap-2 rounded-md text-sm" + role="status" + aria-label={`Approval status: ${status}`} + > {renderStatusContent()} </div> );packages/api/src/router/environment.ts (3)
165-169
: Consider adding error handling for non-existent usersThe changes look good, but consider adding error handling for cases where the provided userId doesn't exist.
.mutation(async ({ ctx, input }) => { + const userExists = await ctx.db + .select() + .from(user) + .where(eq(user.id, input.userId)) + .then(result => result.length > 0); + + if (!userExists) { + throw new Error('User not found'); + } + const envApproval = await ctx.dbAlso applies to: 174-174
225-229
: Apply consistent error handling for user validationSimilar to the approve procedure, consider adding user existence validation here as well.
.mutation(({ ctx, input }) => ctx.db.transaction(async (tx) => { + const userExists = await tx + .select() + .from(user) + .where(eq(user.id, input.userId)) + .then(result => result.length > 0); + + if (!userExists) { + throw new Error('User not found'); + } + await txAlso applies to: 235-235
165-169
: Consider extracting user validation to a reusable functionBoth approve and reject procedures need similar user validation. Consider extracting this to a reusable function to reduce duplication and ensure consistency.
async function validateUser(db: typeof ctx.db, userId: string) { const userExists = await db .select() .from(user) .where(eq(user.id, userId)) .then(result => result.length > 0); if (!userExists) { throw new Error('User not found'); } }This function could be used in both procedures before performing the approval/rejection operations.
Also applies to: 225-229
apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/TargetReleaseTable.tsx (1)
103-105
: Simplify key comparison by removing unnecessaryString
conversionIn the expression
(m) => m.key === String(ReservedMetadataKey.Links)
, theReservedMetadataKey.Links
is already a string. TheString()
conversion is unnecessary and can be removed to simplify the code.Apply this diff to simplify the comparison:
const linksMetadata = job.job.metadata.find( - (m) => m.key === String(ReservedMetadataKey.Links), + (m) => m.key === ReservedMetadataKey.Links, )?.value;
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
📒 Files selected for processing (7)
- apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/PolicyApprovalRow.tsx (2 hunks)
- apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/TargetReleaseTable.tsx (3 hunks)
- apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/page.tsx (1 hunks)
- packages/api/src/router/environment.ts (5 hunks)
- packages/db/drizzle/0024_bouncy_bromley.sql (1 hunks)
- packages/db/drizzle/meta/_journal.json (1 hunks)
- packages/db/src/schema/environment.ts (2 hunks)
🧰 Additional context used
🪛 Biome
apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/TargetReleaseTable.tsx
[error] 101-190: Avoid using unnecessary Fragment.
A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment(lint/complexity/noUselessFragments)
🔇 Additional comments (13)
packages/db/drizzle/0024_bouncy_bromley.sql (1)
2-6
: Review CASCADE DELETE behavior for audit compliance.The current foreign key constraint will delete approval records when a user is deleted. This could be problematic for audit purposes where you need to maintain a history of who approved what, even if the user no longer exists.
Let's verify if this table is used for audit purposes:
Consider these alternatives:
- Use SET NULL instead of CASCADE (requires removing NOT NULL constraint)
- Use a soft delete for users to maintain referential integrity
- Archive user data instead of deleting it
apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/page.tsx (2)
71-71
: LGTM: Environment prop addition.The addition of the
environments
prop toTargetReleaseTable
aligns with the architectural changes, allowing the component to handle environment-specific data and approvals directly.
Line range hint
1-74
: Verify the removal of approval handling logic.The removal of policy approval handling from this component suggests an architectural shift where approval management has been moved to child components. This change could improve separation of concerns, but we should verify that the approval functionality is properly implemented in the new location.
Let's verify the approval handling implementation:
✅ Verification successful
Let me gather more information about the TargetReleaseTable component since the first command failed:
Approval handling has been correctly moved to child components
The removal of approval handling from the page component is a valid architectural improvement. The verification shows that:
- TargetReleaseTable fetches approvals via
api.environment.policy.approval.byReleaseId
and passes them to PolicyApprovalRow- PolicyApprovalRow handles all approval-related actions (approve/reject) with proper mutations and UI feedback
- The addition of the
environments
prop to TargetReleaseTable is necessary as it's used to render environment-specific approvals🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify that approval handling has been moved to TargetReleaseTable # and ensure no approval-related functionality is lost # Test 1: Check TargetReleaseTable for new approval handling echo "Checking TargetReleaseTable implementation..." rg -A 10 "PolicyApprovalRow|approval" --type typescript # Test 2: Verify no orphaned approval-related code echo "Checking for any orphaned approval-related code..." ast-grep --pattern 'api.environment.policy.approval'Length of output: 1616
Script:
#!/bin/bash # Check the implementation of TargetReleaseTable and related components echo "Checking TargetReleaseTable content..." cat apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/TargetReleaseTable.tsx echo "\nChecking PolicyApprovalRow content..." cat apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/PolicyApprovalRow.tsxLength of output: 10640
apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/PolicyApprovalRow.tsx (1)
17-18
: Props refactoring improves type safety.The props restructuring with optional user info and single environment improves type safety and better reflects the component's requirements.
packages/db/drizzle/meta/_journal.json (1)
172-178
: LGTM! Migration journal entry is properly structured.The new entry follows the established format and maintains sequential ordering.
Let's verify the corresponding migration file exists and contains the expected user tracking changes:
✅ Verification successful
Migration file verified and contains expected changes ✅
The migration file exists at the correct location and implements the expected schema changes:
- Adds
user_id
column (UUID) toenvironment_policy_approval
table- Sets up foreign key constraint referencing the
user
table with proper cascade behavior🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify migration file exists and contains user_id column addition # Test 1: Check if migration file exists fd "0024_bouncy_bromley.sql" --type f # Test 2: Verify migration content includes user_id column addition rg -A 5 "user_id" "packages/db/drizzle/0024_bouncy_bromley.sql"Length of output: 503
packages/db/src/schema/environment.ts (1)
22-22
: LGTM!The import is correctly placed and follows the established import pattern.
packages/api/src/router/environment.ts (2)
34-34
: LGTM: Import added for user schemaThe addition of the user schema import aligns with the new user tracking functionality.
126-126
: Verify impact of optional status on existing consumersThe changes to make status optional and include user information look good, but we should verify the impact on existing consumers of this API.
Also applies to: 137-137, 140-145, 152-152
✅ Verification successful
Making status optional is safe for existing consumers
Based on the codebase analysis:
- The database schema maintains
status
asNOT NULL
with a default value of 'pending'- Frontend components handle the status properly:
TargetReleaseTable.tsx
safely filters and types the approvals dataFlowPolicyNode.tsx
has proper null checks and status handling- The job dispatcher correctly checks status with
environment_policy_approval?.status === "approved"
Making the status optional in the input schema while keeping it required in the database (with a default) is a safe change that won't impact existing consumers.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for direct usage of byReleaseId to verify consumers handle optional status rg -A 5 "byReleaseId.*status.*" --type tsLength of output: 43
Script:
#!/bin/bash # Let's first find all references to byReleaseId function rg -A 5 "byReleaseId" --type ts # Also search for any router endpoints that might be consuming this rg -A 5 "router\.(get|post|put|delete).*release.*approval" --type ts # Let's also check the schema usage ast-grep --pattern 'z.enum(["pending", "approved", "rejected"])'Length of output: 7179
Script:
#!/bin/bash # Let's check how the approval data is being used in the frontend components rg -B 5 -A 10 "environmentApprovals" --type tsx # Also check for any direct status handling rg -B 2 -A 5 "approval.*status" --type tsxLength of output: 148
Script:
#!/bin/bash # Let's check how the approval data is being used in the frontend components, without file type restriction rg -B 5 -A 10 "environmentApprovals" # Also check for any direct status handling rg -B 2 -A 5 "approval.*status" # Let's also check the component that uses the byReleaseId query rg -B 10 -A 10 "policy\.approval\.byReleaseId\.useQuery"Length of output: 45790
apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/TargetReleaseTable.tsx (5)
72-97
: Great implementation of collapsible environment rowsThe use of
useState
for controlling the expansion of environment rows and the clean rendering logic enhance the user experience by making the interface more interactive and intuitive.
100-191
: Conditional rendering is handled correctlyThe conditional rendering of job details based on the
isExpanded
state is implemented well. The mapping overjobs
to display each job's information ensures that users can access detailed data when needed.🧰 Tools
🪛 Biome
[error] 101-190: Avoid using unnecessary Fragment.
A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment(lint/complexity/noUselessFragments)
89-96
: Proper handling of policy approvalsDisplaying policy approvals using the
PolicyApprovalRow
component with appropriate props ensures that approval statuses are accurately represented for each environment.
148-168
: Robust handling of external linksThe code safely renders external links with the correct
target
andrel
attributes, which is important for security and preventing potential vulnerabilities like reverse tabnabbing.
Line range hint
196-221
: Efficient rendering ofTargetReleaseTable
Iterating over
environments
to renderCollapsibleTableRow
components makes theTargetReleaseTable
dynamic and scalable for multiple environments.🧰 Tools
🪛 Biome
[error] 101-190: Avoid using unnecessary Fragment.
A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment(lint/complexity/noUselessFragments)
...systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/PolicyApprovalRow.tsx
Outdated
Show resolved
Hide resolved
...systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/PolicyApprovalRow.tsx
Outdated
Show resolved
Hide resolved
...systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/PolicyApprovalRow.tsx
Outdated
Show resolved
Hide resolved
…eployments/[deploymentSlug]/releases/[versionId]/PolicyApprovalRow.tsx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Zachary Blasczyk <[email protected]>
@coderabbitai Please re-review the code. I want to keep the userId nullable because it is only set when keeping track of the approver/rejector. |
@zacharyblasczyk: Understood, I will re-review the code considering your decision to keep (=・ω・=)? ✏️ Learnings added
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
📒 Files selected for processing (3)
- apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/FlowPolicyNode.tsx (3 hunks)
- apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/PolicyApprovalRow.tsx (3 hunks)
- packages/db/drizzle/meta/_journal.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/PolicyApprovalRow.tsx
- packages/db/drizzle/meta/_journal.json
...g]/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/FlowPolicyNode.tsx
Show resolved
Hide resolved
...g]/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/FlowPolicyNode.tsx
Show resolved
Hide resolved
...g]/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/FlowPolicyNode.tsx
Show resolved
Hide resolved
...systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/PolicyApprovalRow.tsx
Outdated
Show resolved
Hide resolved
...systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/PolicyApprovalRow.tsx
Outdated
Show resolved
Hide resolved
...ystems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/TargetReleaseTable.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Outside diff range and nitpick comments (3)
apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/PolicyApprovalRow.tsx (1)
27-30
: Replace console.error with proper error logging.While the validation is good, using console.error in production code isn't recommended.
Consider using a proper error logging system or error boundary:
if (!environment) { - console.error("Environment is undefined for approval:", approval); + // TODO: Replace with your error logging system + logger.error("Environment is undefined for approval", { approval }); return null; }apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/TargetReleaseTable.tsx (2)
43-58
: Consider adding loading state for approvals query.While there's a loading state for jobs, the approvals query loading state isn't handled. This could lead to a jarring user experience if approvals load after the component is rendered.
Consider showing a loading indicator or skeleton for approvals while they're loading:
+ if (approvals.isLoading) { + return ( + <div className="flex items-center gap-2"> + <IconLoader2 className="h-4 w-4 animate-spin" /> + </div> + ); + }
Line range hint
37-190
: Consider memoizing the CollapsibleTableRow component.The component could benefit from memoization to prevent unnecessary re-renders, especially when multiple rows are rendered.
Consider wrapping the component with React.memo:
-const CollapsibleTableRow: React.FC<CollapsibleTableRowProps> = ({ +const CollapsibleTableRow = React.memo(function CollapsibleTableRow({ environment, deploymentName, release, -}) => { +}: CollapsibleTableRowProps) { // ... existing code -}; +});🧰 Tools
🪛 Biome
[error] 97-186: Avoid using unnecessary Fragment.
A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment(lint/complexity/noUselessFragments)
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
📒 Files selected for processing (3)
- apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/PolicyApprovalRow.tsx (3 hunks)
- apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/TargetReleaseTable.tsx (3 hunks)
- packages/api/src/router/environment.ts (5 hunks)
🧰 Additional context used
🪛 Biome
apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/TargetReleaseTable.tsx
[error] 97-186: Avoid using unnecessary Fragment.
A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment(lint/complexity/noUselessFragments)
🔇 Additional comments (11)
apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/PolicyApprovalRow.tsx (5)
6-6
: LGTM: Type changes improve type safety.The updated types properly reflect the nullable user information and single environment requirement.
Also applies to: 16-17
32-34
: LGTM: Clean variable declarations.The destructuring and data fetching logic is well-organized.
42-42
: LGTM: Improved user feedback messages.The toast messages now properly reference the environment name and provide clear feedback.
Also applies to: 53-53
58-69
: LGTM: Concise action handlers.The mutation handlers are well-structured and maintain consistent behavior.
72-97
: 🛠️ Refactor suggestionConsider extracting status-specific components.
I agree with the previous review comment about moving these to their own components. This would improve maintainability and reusability.
Consider this structure:
const PendingActions: React.FC<{ onApprove: () => void; onReject: () => void }> = ... const ApprovalStatus: React.FC<{ status: string; userName?: string }> = ... // In main component return ( <div className="flex items-center gap-2 rounded-md text-sm"> {status === "pending" ? ( <PendingActions onApprove={handleApprove} onReject={handleReject} /> ) : ( <ApprovalStatus status={status} userName={approval.user?.name} /> )} </div> );apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/TargetReleaseTable.tsx (3)
Line range hint
3-34
: LGTM! Well-structured type definitions and imports.The type definitions are clear and properly typed, with good organization of imports.
192-216
: LGTM! Clean implementation of the table wrapper.The component is well-structured and properly handles the mapping of environments to CollapsibleTableRow components.
97-187
:⚠️ Potential issueRemove unnecessary Fragment wrapper.
The Fragment wrapper around the conditional rendering is redundant since it contains only one child.
Replace the Fragment with the direct conditional rendering:
- {isExpanded && ( - <> + {isExpanded && jobs?.map((job, idx) => { // ... existing code - </> - )} + })}Likely invalid or redundant comment.
🧰 Tools
🪛 Biome
[error] 97-186: Avoid using unnecessary Fragment.
A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment(lint/complexity/noUselessFragments)
packages/api/src/router/environment.ts (3)
34-34
: LGTM: User schema import added correctlyThe import is properly placed and will support the new user tracking functionality.
Line range hint
126-152
: LGTM: Enhanced query flexibility and user trackingThe changes improve the query flexibility by making the status filter optional and properly integrate user tracking through a left join. The nullable user relationship aligns with the PR objective of tracking approvers/rejectors.
225-235
: LGTM: Consistent user tracking in rejection processThe changes maintain consistency with the approval process by implementing the same user tracking pattern while preserving the existing transaction and job cancellation logic.
Summary by CodeRabbit
Release Notes
New Features
PolicyApprovalRow
component to include user information for approvals and improved status rendering.CollapsibleTableRow
for a more interactiveTargetReleaseTable
, allowing users to expand rows for detailed job data.Bug Fixes
ReleasePage
, streamlining the user experience.Chores
user_id
column and foreign key constraints.